package com.dwy2002.common.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSException;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.dwy2002.common.config.MyOssConfig;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.dwy2002.enums.FileCompressSettings.*;
import static com.dwy2002.enums.OssEnum.*;


/**
 * 阿里云oss工具类
 *
 * @author 杜伟毅
 * @version 1.0
 * @since 2025/01/18
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class OssUtils {
    private static String BUCKET_NAME;
    private static String ALI_DOMAIN;
    private final MyOssConfig myOssConfig;
    private final OSS ossClient;

    /**
     * 从完整OSS URL中提取Bucket内的相对路径
     * <p>
     * 示例输入: https://test-dwy.oss-cn-shenzhen.aliyuncs.com/Cloud-Water-Tea-Appointment/courses_images/xxx.jpeg
     * 示例输出: Cloud-Water-Tea-Appointment/courses_images/xxx.jpeg
     *
     * @param imageUrl 图片链接
     * @return 对函数返回值的描述
     * @author 杜伟毅
     * @since 2025/5/7 0007 21:49
     */

    private static String extractObjectPathFromUrl(String imageUrl) {
        try {
            // 移除协议头和域名部分
            String withoutProtocol = imageUrl.replaceFirst("^https?://[^/]+/", "");
            // URL解码（处理可能存在的编码字符）
            return URLDecoder.decode(withoutProtocol, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("从URL提取OSS对象路径失败: {}", imageUrl, e);
            return null;
        }
    }

    /**
     * 生成一个随机的文件名，并将图片统一改成jpeg
     *
     * @param file 图片文件
     * @return 随机文件名.jpeg
     * @author 杜伟毅
     * @since 2025/1/19 0019 0:15
     */
    private static String generatorFileName(MultipartFile file) {
        return UUID.randomUUID().toString().replace("-", "") + ".jpeg";
    }

    /**
     * 图片压缩的功能
     *
     * @param imageBytes 原始图片的字节数组
     * @param scale      压缩比例，例如0.2表示压缩至20%
     * @author 杜伟毅
     * @since 2025/1/20 0020 16:49
     */
    private static byte[] compressImage(byte[] imageBytes, double scale) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        Thumbnails.of(new ByteArrayInputStream(imageBytes))
                .scale(scale)
                .toOutputStream(outputStream);
        return outputStream.toByteArray();
    }

    /**
     * 检查文件是否超过设定值
     *
     * @param fileSize 文件大小
     * @return 是否超过设定的值，超过为true，没超过为false
     * @author 杜伟毅
     * @since 2025/1/20 0020 17:51
     */
    public static boolean isFileOver(long fileSize, double num) {
        double maxFileSize = num * 1024 * 1024;
        return fileSize > maxFileSize;
    }

    //----------------------------------------------------------------------------------

    /**
     * 根据文件大小选择压缩数值，
     *
     * @param fileSize 文件大小
     * @return 压缩数值
     * @author 杜伟毅
     * @since 2025/2/28 0028 15:42
     */
    private static double determineCompressionQuality(long fileSize) {
        // 配置表（阈值单位需与 fileSize 一致）
        List<CompressionRule> rules = Arrays.asList(
                new CompressionRule(15 * 1024 * 1024, MEDIUM_COMPRESSION_QUALITY.getNum()), // 15MB → 0.5
                new CompressionRule(8 * 1024 * 1024, HIGH_COMPRESSION_QUALITY.getNum()),   // 8MB → 0.3
                new CompressionRule(1024L * 100, DEFAULT_COMPRESSION_QUALITY.getNum())     // 0.1MB → 1
        );

        return rules.stream()
                .filter(rule -> fileSize > rule.threshold())
                .findFirst()
                .map(CompressionRule::quality)
                .orElse(DEFAULT_COMPRESSION_QUALITY.getNum()); // 默认质量
    }

    /**
     * 上传用户头像
     *
     * @param file 图片文件
     * @author 杜伟毅
     * @since 2025/1/19 0019 0:12
     */
    public String uploadAvatar(MultipartFile file) throws IOException {
        String fileName = generatorFileName(file);
        byte[] compressedImageBytes = new byte[]{};
        //文件数组
        byte[] fileBytes = file.getBytes();
        //文件大小
        long fileSize = file.getSize();
        try {
            if (isFileOver(fileSize, 5)) {
                compressedImageBytes = compressImage(fileBytes, HIGH_COMPRESSION_QUALITY.getNum());
            } else if (isFileOver(fileSize, 2)) {
                compressedImageBytes = compressImage(fileBytes, MEDIUM_COMPRESSION_QUALITY.getNum());
            } else if (isFileOver(fileSize, 0.1)) {
                compressedImageBytes = compressImage(fileBytes, DEFAULT_COMPRESSION_QUALITY.getNum());
            }
        } catch (IOException e) {
            throw new IOException("Failed to process image file: " + e.getMessage(), e);
        }
        // OSS客户端对象
        ossClient.putObject(
                BUCKET_NAME, // 仓库名
                AVATAR + fileName, // 路径+文件名
                new ByteArrayInputStream(compressedImageBytes) // 上传压缩后的图片
        );
        return ALI_DOMAIN + AVATAR + fileName;
//        实例：https://test-dwy.oss-cn-shenzhen.aliyuncs.com/Cloud-Water-Tea-Appointment/avatar/04bd0b4ddb0444f088e45a659eb17640.jpeg
    }

    /**
     * 上传图片的通用方法
     *
     * @param files  单个或多个图片文件
     * @param prefix 图片路径前缀（如 "courses/" 或 "room/"）
     * @author 杜伟毅
     * @since 2025/1/26 0026 16:41
     */
    public List<String> uploadImages(List<MultipartFile> files, String prefix) {
        // 参数校验
        if (Utils.isListEmpty(files)) {
            throw new IllegalArgumentException("文件列表不能为空");
        }

        // 过滤无效文件
        files = files.stream()
                .filter(file -> !file.isEmpty() && file.getSize() > 0)
                .collect(Collectors.toList());

        if (files.isEmpty()) {
            throw new IllegalArgumentException("没有有效的文件可上传");
        }


        // 使用普通流处理
        List<String> imageUrls = files.stream().map(file -> {
            try {
                String fileName = generatorFileName(file);
                byte[] fileBytes = file.getBytes();
                long fileSize = file.getSize();

                // 根据文件大小选择压缩质量
                double quality = determineCompressionQuality(fileSize);
                byte[] compressedBytes = compressImage(fileBytes, quality);

                // 上传到 OSS
                ossClient.putObject(
                        BUCKET_NAME,
                        prefix + fileName,
                        new ByteArrayInputStream(compressedBytes)
                );
                return ALI_DOMAIN + prefix + fileName;
            } catch (IOException e) {
                log.error("处理文件 {} 失败: {}", file.getOriginalFilename(), e.getMessage());
                throw new UncheckedIOException("Failed to process file: " + file.getOriginalFilename(), e);
            }
        }).collect(Collectors.toList());

        return imageUrls;
    }

    /**
     * 删除指定图片
     *
     * @param oldImage 数据库的旧的图片链接
     * @author 杜伟毅
     * @since 2025/1/19 0019 13:30
     */
    public void deleteImage(String oldImage) {
        if (Utils.isEmptyOrNull(oldImage)) {
            return;
        }
        // 如果是默认头像，可以选择不删除或执行其他操作
        if (oldImage.equals(DEFAULT_AVATAR_URL.getUrl()) || oldImage.equals(DEFAULT_URL.getUrl())) {
            return;
        }
        // 截取文件名
        String objectName = oldImage.substring(46);

        try {
            // 开始删除
            ossClient.deleteObject(BUCKET_NAME, objectName);
        } catch (OSSException e) {
            log.error("OSS 错误码: {}, 错误信息: {}", e.getErrorCode(), e.getErrorMessage());
        }
    }

    /**
     * 检查图片是否存在于OSS
     *
     * @param imageUrl 图片完整URL
     * @return true-存在，false-不存在
     */
    public boolean isImageExist(String imageUrl) {
        if (StringUtils.isEmpty(imageUrl)) {
            return false;
        }

        // 从完整URL中提取Bucket内的相对路径
        String objectPath = extractObjectPathFromUrl(imageUrl);
        if (StringUtils.isEmpty(objectPath)) {
            return false;
        }

        try {
            return ossClient.doesObjectExist(BUCKET_NAME, objectPath);
        } catch (Exception e) {
            log.error("检查OSS图片是否存在异常: {}", imageUrl, e);
            return false;
        }
    }

    @PostConstruct
    public void init() {
        BUCKET_NAME = myOssConfig.getBucketName();
        ALI_DOMAIN = myOssConfig.getAltDomain();
    }


    /**
     * 存储配置表，压缩质量配置化
     *
     * @param threshold 文件大小
     * @param quality   压缩质量
     */
    private record CompressionRule(long threshold, double quality) {
    }
}